﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.Windows.Design.PropertyEditing;
using System.ComponentModel;

namespace DenisVuyka.Controls.PropertyGrid
{
  using Data;

  public class CategoryItem : CategoryEntry
  {
    #region Properties

    /// <summary>
    /// Gets or sets the attribute the category was created with.
    /// </summary>
    /// <value>The attribute.</value>
    public Attribute Attribute { get; set; }

    private bool isExpanded = true;
    /// <summary>
    /// Gets or sets a value indicating whether this category is expanded.
    /// </summary>
    /// <value>
    /// 	<c>true</c> if this category is expanded; otherwise, <c>false</c>.
    /// </value>
    public bool IsExpanded
    {
      get { return isExpanded; }
      set
      {
        if (isExpanded == value) return;
        isExpanded = value;
        OnPropertyChanged("IsExpanded");
      }
    }

    private ObservableList<PropertyEntry> properties = new ObservableList<PropertyEntry>();
    /// <summary>
    /// Get all the properties in the category.
    /// </summary>
    /// <value></value>
    /// <returns>
    /// An enumerable collection of all the properties in the category.
    /// </returns>
    public override IEnumerable<PropertyEntry> Properties
    {
      get { return properties; }
    }

    /// <summary>
    /// Gets the <see cref="Microsoft.Windows.Design.PropertyEditing.PropertyEntry"/> with the specified property name.
    /// </summary>
    /// <value></value>
    public override PropertyEntry this[string propertyName]
    {
      get { return this.Properties.Where(entry => entry.PropertyName == propertyName).FirstOrDefault(); }
    }

    private IComparer<PropertyEntry> comparer = new PropertyEntryComparer();
    /// <summary>
    /// Gets or sets the comparer used to sort properties.
    /// </summary>
    /// <value>The comparer. </value>
    public IComparer<PropertyEntry> Comparer
    {
      get { return comparer; }
      set
      {
        if (comparer == value) return;
        comparer = value;
        properties.Sort(comparer);
        OnPropertyChanged("Comparer");
      }
    }

    private bool _PropertiesMatchFilter = true;
    /// <summary>
    /// Gets or sets a value indicating whether properties match filter.
    /// </summary>
    /// <value>
    /// 	<c>true</c> if properties match filter; otherwise, <c>false</c>.
    /// </value>
    public bool PropertiesMatchFilter
    {
      get { return _PropertiesMatchFilter; }
      set
      {
        if (_PropertiesMatchFilter == value) return;
        _PropertiesMatchFilter = value;
        OnPropertyChanged("PropertiesMatchFilter");
      }
    }
    #endregion

    #region ctor
    public CategoryItem(string name) : base(name) { }

    public CategoryItem(CategoryAttribute category)
      : base(category.Category)
    {
      this.Attribute = category;
    }
    #endregion

    /// <summary>
    /// Adds the property.
    /// </summary>
    /// <param name="property">The property.</param>
    public void AddProperty(PropertyEntry property)
    {
      Debug.Assert(property != null, "Property is null!");
     
      int index = this.properties.BinarySearch(property, comparer);
      if (index < 0)
        index = ~index;

      this.properties.Insert(index, property);
    }

    /// <summary>
    /// Indicates whether a <see cref="T:Microsoft.Windows.Design.PropertyEditing.CategoryEntry"/> matches a predicate.
    /// </summary>
    /// <param name="predicate">The predicate to compare to.</param>
    /// <returns>
    /// true if the <see cref="T:Microsoft.Windows.Design.PropertyEditing.CategoryEntry"/> matches the predicate; otherwise, false.
    /// </returns>
    public override bool MatchesPredicate(PropertyFilterPredicate predicate)
    {       
      return predicate.Match(base.CategoryName);
    }

    /// <summary>
    /// Determines if the <see cref="T:Microsoft.Windows.Design.PropertyEditing.CategoryEntry"/> matches a filter and stores the information.
    /// </summary>
    /// <param name="filter">The filter to compare to.</param>
    public override void ApplyFilter(PropertyFilter filter)
    {
      this.MatchesFilter = (filter == null || filter.Match(this));
      bool propertiesMatch = false;
      foreach (PropertyEntry entry in this.Properties)
      {
        if (this.PropertyMatchesFilter(filter, entry))
          propertiesMatch = true;
      }
      this.PropertiesMatchFilter = propertiesMatch;
      this.OnFilterApplied(filter);      
    }
        
    private bool PropertyMatchesFilter(PropertyFilter filter, PropertyEntry entry)
    {
      entry.ApplyFilter(filter);
      return entry.MatchesFilter;
    }
  }
}